package com.shujia.jinjie

import scala.collection.immutable
import scala.io.Source

object HomeWork {

  //定义一些变量，样例类，为后续的开发做准备

  /**
   * 定义一些样例类  类似于java的封装
   */
  private case class Student(id: String, name: String, age: Int, gender: String, clazz: String)

  private case class Score(id: String, subject_id: String, score: Int)

  private case class Subject(subject_id: String, subject_name: String, subject_score: Int)

  /**
   * 定义三个存储三个不同对象的List集合
   */
  private var stuList: List[Student] = _
  private var scoreList: List[Score] = _
  private var subjectList: List[Subject] = _

  /**
   * 定义一些map集合
   * stuInfoMap  (stu.id, stu.name + "," + stu.clazz)
   * subNameMap  (subject.subject_id, subject.subject_name)
   * subScoreMap (subject.subject_id, subject.subject_score)
   */
  private var stuInfoMap: Map[String, String] = _
  private var subNameMap: Map[String, String] = _
  private var subScoreMap: Map[String, Int] = _

  //初始化变量
  private def loadData(): Unit = {
    //读取学生数据
    stuList = "scala/data/students.txt".load()
      .map {
        case Array(id: String, name: String, age: String, gender: String, clazz: String) =>
          Student(id, name, age.toInt, gender, clazz)
      }

    //读取成绩数据
    scoreList = "scala/data/score.txt".load()
      .map {
        case Array(id: String, subject_id: String, score: String) =>
          Score(id, subject_id, score.toInt)
      }

    //读取科目数据
    subjectList = "scala/data/subject.txt".load()
      .map {
        case Array(subject_id: String, subject_name: String, subject_score: String) =>
          Subject(subject_id, subject_name, subject_score.toInt)
      }

    //处理三个map集合
    //stuInfoMap 存储了学生的学号为键，姓名和班级作为值
    stuInfoMap = stuList.map((stu: Student) => (stu.id, stu.name + "," + stu.clazz)).toMap
    subNameMap = subjectList.map((sub: Subject) => (sub.subject_id, sub.subject_name)).toMap
    subScoreMap = subjectList.map((sub: Subject) => (sub.subject_id, sub.subject_score)).toMap
  }

  /**
   * 根据学号集合打印【学号，姓名，班级，科目，分数】
   */
  private def printStudentInfoWithId(ids: List[String]): Unit = {
    // 从总的学生成绩信息中过滤出来
    scoreList.filter((sco: Score) => ids.contains(sco.id))
      .map {
        case Score(id: String, subject_id: String, score: Int) => {
          //根据学号，查找姓名和班级 stuInfoMap
          val nameWithClazz: String = stuInfoMap.getOrElse(id, "查无此人")
          //根据科目编号，查找科目的名字 subNameMap
          val subject_name: String = subNameMap.getOrElse(subject_id, "无此科目")
          s"[$id,$nameWithClazz,$subject_name,$score]"
        }
      }.foreach(println)
  }


  /**
   * 统计班级人数 [班级,人数]
   */
  private def xuQiu1(): Unit = {
    stuList.groupBy((stu: Student) => stu.clazz)
      .map((kv: (String, List[Student])) => {
        s"[${kv._1},${kv._2.size}]"
      }).foreach(println)

  }

  /**
   * 统计学生的总分 [学号,学生姓名,学生年龄,总分]
   */
  private def xuQiu2(): Unit = {
    val stringToInt: Map[String, Int] = scoreList.groupBy((sco: Score) => sco.id)
      .map((kv: (String, List[Score])) => {
        (kv._1, kv._2.map(_.score).sum)
      })

    val ids: List[String] = stringToInt.map {
      case (id: String, _: Int) => id
    }.toList

    stuList.filter((stu: Student) => ids.contains(stu.id))
      .map((stu: Student) => {
        val sumScore: Int = stringToInt.getOrElse(stu.id, 0)
        s"[${stu.id},${stu.name},${stu.age},$sumScore]"
      }).foreach(println)
  }


  /**
   * 统计年级排名前十学生各科的分数 [学号，姓名，班级，科目，分数]
   */
  private def xuQiu3(): Unit = {
    val ids: List[String] = scoreList.groupBy((s: Score) => s.id) // 按照学号进行分组
      .map((kv: (String, List[Score])) => {
        (kv._1, kv._2.map(_.score).sum) // 求每个学生的总分
      })
      .toList
      .sortBy(-_._2)
      .take(10)
      .map(_._1)

    printStudentInfoWithId(ids) // 学号，姓名，班级，科目，分数
  }

  /**
   * 统计总分大于年级平均分的学生 [学号，姓名，班级，总分]
   */
  private def xuQiu4(): Unit = {
    //先计算年级平均分 372
    val avgScore: Int = scoreList.map(_.score).sum / stuList.size
    //    println(avgScore)

    //计算每个人总分进行过滤
//    val map: Map[String, List[Score]] = scoreList.groupBy((s: Score) => s.id)

    scoreList.groupBy((s: Score) => s.id) // 按照学号进行分组
      .map((kv: (String, List[Score])) => {
        (kv._1, kv._2.map(_.score).sum) // 求每个学生的总分
      })
      .filter(_._2 > avgScore)
      .map((t: (String, Int)) => {
        //根据stuInfoMap获取学生的姓名和班级
        val nameWithClazz: String = stuInfoMap.getOrElse(t._1, "查无此人")
        s"[${t._1},$nameWithClazz,${t._2}]"
      }).foreach(println)
  }

  /**
   * 统计每科都及格的学生 [学号，姓名，班级，科目，分数]
   */
  private def xuQiu5(): Unit = {
    //1500100001,1000001,98
    val ids: List[String] = scoreList.filter((sco: Score) => sco.score >= subScoreMap.getOrElse(sco.subject_id, 0) * 0.6)
      .groupBy(_.id) // 根据学号分组，过滤6门考试都及格的学生
      .filter(_._2.size == 6)
      .keys
      .toList

    printStudentInfoWithId(ids)
  }

  /**
   * 统计每个班级的前三名 [学号，姓名，班级，分数]
   */
  private def xuQiu6(): Unit = {
    val ids: List[String] = scoreList
      .groupBy((s: Score) => s.id) // 按照学号进行分组
      .map((kv: (String, List[Score])) => {
        val nameWithClazz: String = stuInfoMap.getOrElse(kv._1, "查无此人")
        val infos: Array[String] = nameWithClazz.split(",")
        val name: String = infos(0)
        val clazz: String = infos(1)
        (kv._1, name, clazz, kv._2.map(_.score).sum) // 求每个学生的总分
      })
      .groupBy(_._3) // 根据班级进行分组
      .flatMap((kv: (String, Iterable[(String, String, String, Int)])) => {
        kv._2.toList.sortBy(-_._4).take(3)
      }).map(_._1).toList

    // 从总的学生成绩信息中过滤出来
    scoreList.filter((sco: Score) => ids.contains(sco.id))
      .map {
        case Score(id: String, subject_id: String, score: Int) => {
          //根据学号，查找姓名和班级 stuInfoMap
          val nameWithClazz: String = stuInfoMap.getOrElse(id, "查无此人")
          s"[$id,$nameWithClazz,$score]"
        }
      }.foreach(println)
  }

  /**
   * 统计偏科最严重的前100名学生  [学号，姓名，班级，科目，分数]
   *
   * 方差
   * （每个人的各科分数-6门考试的平均分）^2 / 科目数
   *
   */
  private def xuQiu7(): Unit = {
    //归一化
    val ids: List[String] = scoreList.map {
      case Score(id: String, subject_id: String, score: Int) =>
        (id: String, subject_id: String, score * 100 / subScoreMap.getOrElse(subject_id, 0))
    }.groupBy(_._1) //根据学号进行分组
      .map((kv: (String, List[(String, String, Int)])) => {
        val id: String = kv._1
        val scoreList: List[(String, String, Int)] = kv._2
        //每个人的平均分
        val avgScore: Int = scoreList.map(_._3).sum / scoreList.size
        //求方差
        val fangCha: Double = scoreList.map((t3: (String, String, Int)) => Math.pow(t3._3 - avgScore, 2)).sum / scoreList.size
        (id, fangCha)
      })
      .toList
      .sortBy(-_._2)
      .take(100)
      .map(_._1)

    printStudentInfoWithId(ids)
  }

  def main(args: Array[String]): Unit = {
    loadData()

    //    xuQiu1()
    //    xuQiu2()
    //    xuQiu3()
    //    xuQiu4()
    //    xuQiu5()
    //    xuQiu6()
    xuQiu7()
  }

  //定义一个隐式转换类
  //将来可以直接通过文件路径获取一个封装了行数据的集合
  implicit class Load(path: String) {
    def load(): List[Array[String]] = {
      Source.fromFile(path).getLines().toList.map((line: String) => line.split(","))
    }
  }

}
